package com.lcy.struct.bTreePlus.disk.core;

import com.lcy.struct.bTreePlus.disk.core.engine.BPlusTreeAddEngine;
import com.lcy.struct.bTreePlus.disk.core.engine.BPlusTreeDelEngine;
import com.lcy.struct.bTreePlus.disk.core.engine.BPlusTreeUpdateEngine;
import com.lcy.struct.bTreePlus.disk.enums.BplusTreeEnums;
import com.lcy.struct.bTreePlus.disk.exception.DuplicateIndexException;
import com.lcy.struct.bTreePlus.disk.index.BTreeIndex;
import com.lcy.struct.bTreePlus.disk.item.TreeNode;
import com.lcy.struct.bTreePlus.disk.log.LogUtils;
import com.lcy.struct.bTreePlus.disk.recovery.RecoveryCenter;
import com.lcy.struct.bTreePlus.disk.recovery.transation.TransactionCenter;
import com.lcy.struct.bTreePlus.entity.BplusTreeTransactionInfo;
import com.lcy.struct.bTreePlus.property.BPlusFileInfo;

import java.nio.channels.FileChannel;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author lucongyang
 * @date Created in  17:09
 * @description
 */
public class BPlusTreeFactory {
    private static Map<Long, BPlusTree> map = new HashMap<>();
    private static Map<String, FileChannel> channelMap = new HashMap<>();

    /**
     * 持久化文件头部重要信息
     */
    public static void saveHeaderInfo() {
        BPlusFileInfo.saveEndPosition();
        BPlusFileInfo.saveRootPosition();
    }

    /**
     * 获取数节点
     */
    public static BPlusTree getTree(long position) {
        if (map.containsKey(position)) {
            return map.get(position);
        }
        BPlusTree bPlusTree = BPlusTree.deserialize(position);
        map.put(position, bPlusTree);
        return bPlusTree;
    }

    /**
     * 将节点加入缓存
     */
    public static void addCache(BPlusTree bPlusTree) {
        map.put(bPlusTree.getPosition(), bPlusTree);
    }

    public static void removeCache(BPlusTree bPlusTree) {
        map.remove(bPlusTree.getPosition());
    }

    /**
     * 获取数据
     */
    public static Object findDataOnDisk(List<Comparable> oneIndex) {
        Comparable[] comparables = new Comparable[oneIndex.size()];
        Comparable[] array = oneIndex.toArray(comparables);
        return findDataOnDisk(array);
    }

    /**
     * 获取数据
     */
    public static Object findDataOnDisk(Comparable... index) {
        BPlusTree bPlusTree = findLeafOnDisk(index);
        Integer i = findDataInLeaf(bPlusTree, index);
        return i != null ? bPlusTree.getDataList().get(i) : null;
    }

    public static Integer findDataInLeaf(BPlusTree leaf, Comparable... index) {
        if (leaf != null) {
            BTreeIndex bTreeIndex = new BTreeIndex();
            bTreeIndex.setIndexList(Arrays.asList(index));
            for (int i = 0; i < leaf.getIndexList().size(); i++) {
                int compare = leaf.getIndexList().get(i).compare(bTreeIndex);
                if (compare == 0) {
                    return i;
                }
            }
        }
        return null;
    }

    public static BPlusTree findLeafOnDisk(Comparable... index) {
        if (index == null || index.length == 0) {
            return null;
        }
        BTreeIndex bTreeIndex = new BTreeIndex();
        bTreeIndex.setIndexList(Arrays.asList(index));
        return findLeafOnDisk(bTreeIndex);
    }

    public static BPlusTree findLeafOnDisk(BTreeIndex index) {
        Long rootPosition = BPlusFileInfo.getRootPosition();
        if (rootPosition == null) {
            return null;
        }
        BPlusTree rootTree = BPlusTreeFactory.getTree(rootPosition);
        return findDataOnDisk(index, rootTree);
    }

    /**
     * 新增数据操作
     *
     * @param withTransaction 是否需要事务，如果不需要(false)，报错仍然继续
     */
    public static void batchAdd(List<TreeNode> list, boolean withTransaction) {
        if (list.isEmpty()) {
            return;
        }
        BPlusFileInfo.reloadProperties();
        BPlusTreeAddEngine bPlusTreeAddEngine = BPlusTreeAddEngine.getInstance();
        for (TreeNode treeNode : list) {
            try {
                // 操作
                bPlusTreeAddEngine.addNode(treeNode);
                LogUtils.log("新增{}", treeNode);
            } catch (DuplicateIndexException e) {
                System.out.println("错误：" + e.getMessage());
                LogUtils.log("节点{}新增错误：" + e.getMessage(), treeNode);
                if (withTransaction) {
                    throw new RuntimeException("节点" + treeNode + "新增错误：" + e.getMessage());
                }
            }
        }
        // 写redo
        BplusTreeTransactionInfo bplusTreeTransactionInfo = bPlusTreeAddEngine.getSerializeCacheNeed();
        RecoveryCenter.getInstance().addToRedo(bplusTreeTransactionInfo);
    }

    /**
     * 删除数据操作
     */
    public static boolean batchDel(List<Comparable[]> ids) {
        if (ids.isEmpty()) {
            return false;
        }
        BPlusFileInfo.reloadProperties();
        BPlusTreeDelEngine bPlusTreeDelEngine = BPlusTreeDelEngine.getInstance();
        boolean result = true;
        for (Comparable[] comparable : ids) {
            // 操作
            boolean b = bPlusTreeDelEngine.delData(comparable);
            if (!b) {
                result = false;
            } else {
                LogUtils.log("删除{}", Arrays.toString(comparable));
            }
        }
        // 写redo
        BplusTreeTransactionInfo bplusTreeTransactionInfo = bPlusTreeDelEngine.getSerializeCacheNeed();
        RecoveryCenter.getInstance().addToRedo(bplusTreeTransactionInfo);
        return result;
    }

    /**
     * 批量修改数据操作
     */
    public static void batchUpdateById(List<TreeNode> list) {
        if (list.isEmpty()) {
            return;
        }
        try {
            for (TreeNode treeNode : list) {
                BPlusTreeUpdateEngine.getInstance().updateData(treeNode.getIndex(), treeNode);
            }
            // 写redo
            BplusTreeTransactionInfo bplusTreeTransactionInfo = BPlusTreeUpdateEngine.getInstance().getSerializeCacheNeed();
            RecoveryCenter.getInstance().addToRedo(bplusTreeTransactionInfo);
            LogUtils.log("批量修改数据，修改数据={}", list);
        } catch (Exception e) {
            LogUtils.log("批量修改出错:{}，数据={}", e.getMessage(), list);
            throw new RuntimeException("批量修改出错：" + e.getMessage() + "数据=" + list, e);
        }
    }

    /**
     * 修改数据操作
     */
    public static void update(BTreeIndex bTreeIndex, TreeNode treeNode) {
        try {
            // 操作
            BPlusTreeUpdateEngine.getInstance().updateData(bTreeIndex, treeNode);
            BplusTreeTransactionInfo bplusTreeTransactionInfo = BPlusTreeUpdateEngine.getInstance().getSerializeCacheNeed();
            // 写redo
            RecoveryCenter.getInstance().addToRedo(bplusTreeTransactionInfo);
            LogUtils.log("修改数据，原索引={}，修改数据={}", bTreeIndex.getIndexList(), treeNode);
        } catch (Exception e) {
            LogUtils.log("修改出错:{},索引={}，数据={}", e.getMessage(), bTreeIndex.getIndexList(), treeNode);
            throw new RuntimeException("修改出错：" + e.getMessage() + "索引=" + bTreeIndex.getIndexList() + "，数据=" + treeNode, e);
        }
    }

    private static BPlusTree findDataOnDisk(BTreeIndex bTreeIndex, BPlusTree bPlusTree) {
        if (bPlusTree.isLeaf()) {
            for (int i = 0; i < bPlusTree.getIndexList().size(); i++) {
                int compare = bPlusTree.getIndexList().get(i).compare(bTreeIndex);
                if (compare == 0) {
                    return bPlusTree;
                }
            }
            return null;
        }
        int index = bTreeIndex.findIndex(bPlusTree.getIndexList());
        Long childPosition = bPlusTree.getChildrenPosition().get(index);
        BPlusTree child = BPlusTreeFactory.getTree(childPosition);
        return findDataOnDisk(bTreeIndex, child);
    }
}
